package com.whf.android.jar.net.rx;


import androidx.annotation.NonNull;

import com.whf.android.jar.constants.BaseEntries;
import com.whf.android.jar.net.HttpMethod;
import com.whf.android.jar.net.RestCreator;
import com.whf.android.jar.net.callback.IError;
import com.whf.android.jar.net.callback.IFailure;
import com.whf.android.jar.net.callback.rx.IRxSuccess;
import com.whf.android.jar.net.callback.rx.RxRequestCallbacks;

import java.util.WeakHashMap;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.RequestBody;

/**
 * Created
 *
 * @author 傅令杰
 * @version 2017/4/2
 */

public final class RxRestClient<T> {

    private final WeakHashMap<String, Object> HEADERS;
    private final WeakHashMap<String, Object> PARAMS;
    private final String URL;
    private final RequestBody BODY;
    private final IRxSuccess<T> SUCCESS;
    private final IFailure FAILURE;
    private final IError ERROR;

    RxRestClient(String url,
                 WeakHashMap<String, Object> headers,
                 WeakHashMap<String, Object> params,
                 RequestBody body,
                 IRxSuccess<T> success,
                 IFailure failure,
                 IError error) {
        this.URL = url;
        this.HEADERS = headers;
        this.PARAMS = params;
        this.BODY = body;
        this.SUCCESS = success;
        this.FAILURE = failure;
        this.ERROR = error;
    }

    @NonNull
    public static RxRestClientBuilder builder() {
        return new RxRestClientBuilder<>();
    }

    private void request(@NonNull HttpMethod method) {
        final RxRestService<T> service = RestCreator.getRxRestService();
        Observable<BaseEntries<T>> observable = null;

        switch (method) {
            case GET:
                observable = service.get(URL, PARAMS);
                break;
            case POST:
                observable = service.post(HEADERS, URL, PARAMS);
                break;
            case POST_RAW:
                observable = service.postRaw(HEADERS, URL, BODY);
                break;
            case PUT:
                observable = service.put(HEADERS, URL, PARAMS);
                break;
            case PUT_RAW:
                observable = service.putRaw(HEADERS, URL, BODY);
                break;
            case DELETE:
                observable = service.delete(URL, PARAMS);
                break;
            default:
                break;
        }
        if (observable != null) {
            setSubscribe(observable, getRequestCallback());
        }
    }

    /**
     * Insert observer
     *
     * @param observable:Observable
     * @param observer:Observer
     * @param <T>                   :  T
     */
    protected static <T> void setSubscribe(@NonNull Observable<BaseEntries<T>> observable, RxRequestCallbacks<T> observer) {
        observable.subscribeOn(Schedulers.io())
                //Zi Xian cheng visits the network
                .subscribeOn(Schedulers.newThread())
                //Callback to main thread
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

    @NonNull
    private RxRequestCallbacks<T> getRequestCallback() {
        return new RxRequestCallbacks<>(
                SUCCESS,
                FAILURE,
                ERROR
        );
    }

    public final void get() {
        request(HttpMethod.GET);
    }

    public final void post() {
        if (BODY == null) {
            request(HttpMethod.POST);
        } else {
            if (!PARAMS.isEmpty()) {
                throw new RuntimeException("params must be null!");
            }
            request(HttpMethod.POST_RAW);
        }
    }

    public final void put() {
        if (BODY == null) {
            request(HttpMethod.PUT);
        } else {
            if (!PARAMS.isEmpty()) {
                throw new RuntimeException("params must be null!");
            }
            request(HttpMethod.PUT_RAW);
        }
    }

    public final void delete() {
        request(HttpMethod.DELETE);
    }


}
